﻿
#ifdef WIN32
#include <winsock.h>
#elif __linux__

#endif

#include <thread>
#include <mutex>
#include <list>
#include <unordered_set>
#include <algorithm>
#include "workservice.h"
#include "tcpsocket.h"

#ifdef WIN32
class LoadSocket{
public:
    LoadSocket(){
        WSADATA wsaData;
        struct hostent* remoteHost = NULL;
        char szHostName[128] = {};
        int nResult = -1;
        nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    }

    ~LoadSocket(){
        WSACleanup();
    }
};
static LoadSocket loader;

typedef int addr_len;
#endif

#ifdef __linux__
#include <sys/socket.h>
typedef int SOCKET;
#define INVALID_SOCKET          (~0)
#define SOCKET_ERROR            (-1)
#include <arpa/inet.h>
#include <unistd.h>

#define closesocket(x) ::close(x)
typedef unsigned int addr_len;
#endif

struct TcpServer::Private{
    Private(){
        isActive = false;
        port = -1;
        socket = -1;
    }

    bool isActive;
    std::string host;
    int port;

    SOCKET socket;
    std::recursive_mutex mutex;

    std::list<TcpSocket*> pending;
    std::unordered_set<TcpSocket*> clients;

    std::shared_ptr<std::thread> recvThread;
};

struct TcpSocket::PrivateCreate{
    SOCKET socket;
    std::string peerIp;
    int peerPort;
    std::string localIp;
    int localPort;
    TcpServer* server;
};

struct TcpSocket::Private{
    Private(){
        isActive = false;
        isBind = false;
        localPort = -1;
        peerPort = -1;
        socket = -1;
        server = NULL;
    }

    bool isActive;
    bool isBind;
    std::string localHost;
    int localPort;
    std::string peerHost;
    int peerPort;

    TcpServer* server;

    ByteArray buffer;

    SOCKET socket;

    std::mutex mutex;
    std::shared_ptr<std::thread> recvThread;
};

TcpServer::TcpServer():_P(new Private())
{



}

TcpServer::~TcpServer()
{
    Close();
}

bool TcpServer::Bind(int port,std::string ip)
{
    std::unique_lock<std::recursive_mutex> lock(_P->mutex);

    _P->socket = socket(AF_INET, SOCK_STREAM, 0);
    if (_P->socket == INVALID_SOCKET)
    {
        return false;
    }


    sockaddr_in addr = {};
    addr.sin_family = AF_INET;

#ifdef WIN32
    if(ip.empty()){
        addr.sin_addr.S_un.S_addr = ntohs(INADDR_ANY);
    }
    else{
        addr.sin_addr.S_un.S_addr = inet_addr(ip.c_str());
    }
#endif


#ifdef __linux__
    if(ip.empty()){
        addr.sin_addr.s_addr = ntohs(INADDR_ANY);
    }
    else{
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
    }
#endif


    addr.sin_port = port;
    int size = sizeof(addr);

    if(addr.sin_port == 0){
        srand(time(NULL));
        int ret = 0;
        do
        {
            addr.sin_port = rand();
            ret = bind(_P->socket,(sockaddr*)&addr,size);
        }
        while (ret == SOCKET_ERROR);
    }
    else{
        if(bind(_P->socket,(sockaddr*)&addr,size) == SOCKET_ERROR)
        {
            closesocket(_P->socket);
            return false;
        }
    }

    if(listen(_P->socket,10) == SOCKET_ERROR){
        closesocket(_P->socket);
        return false;
    }


    _P->port = addr.sin_port;
    _P->host = inet_ntoa(addr.sin_addr);
	_P->isActive = true;
    _P->recvThread.reset(new std::thread(std::bind(&TcpServer::RecvLoop,this)));
    
}

int TcpServer::GetPort() const
{
    std::unique_lock<std::recursive_mutex> lock(_P->mutex);
    return _P->port;
}

std::string TcpServer::GetHost() const
{
    std::unique_lock<std::recursive_mutex> lock(_P->mutex);
    return _P->host;
}

void TcpServer::Close()
{
    {
        std::unique_lock<std::recursive_mutex> lock(_P->mutex);
        if(!_P->isActive) return;
        _P->isActive = false;

        for(auto i:_P->clients){
            delete i;
        }

        closesocket(_P->socket);
    }

    _P->recvThread->join();

    {
        std::unique_lock<std::recursive_mutex> lock(_P->mutex);
        _P->recvThread.reset();
        _P->host = "";
        _P->port = -1;
        _P->socket = -1;
    }
}

bool TcpServer::isActive() const
{
    std::unique_lock<std::recursive_mutex> lock(_P->mutex);
    return _P->isActive;
}

bool TcpServer::hasPendingConnection() const
{
    std::unique_lock<std::recursive_mutex> lock(_P->mutex);
    return !_P->pending.empty();
}

TcpSocket *TcpServer::pendingConnection()
{
	std::unique_lock<std::recursive_mutex> lock(_P->mutex);
    if(_P->pending.empty()){
        return NULL;
    }

    auto ret = _P->pending.front();
    _P->pending.pop_front();
    return ret;
}

void TcpServer::RecvLoop()
{
    while (true) {
        struct timeval t;
        fd_set rset;
        int max_fd;
        t.tv_sec = 60;
        t.tv_usec = 0;

        FD_ZERO(&rset);

        max_fd = _P->socket;
        FD_SET(_P->socket,&rset);

        if(!_P->isActive){
            return;
        }

        int ret = select(max_fd + 1,&rset,NULL,NULL,&t);

        if(!_P->isActive){
            return;
        }

        if(ret == -1){
            continue;
        }

        if(ret == 0){
            continue;
        }

        if(FD_ISSET(_P->socket,&rset)){
            addr_len l = sizeof(sockaddr_in);
            sockaddr_in client_addr = {};
            SOCKET client = accept(_P->socket,(sockaddr*)&client_addr,&l);

            if(client == -1){
                continue;
            }

            TcpSocket::PrivateCreate p;
            p.peerIp = inet_ntoa(client_addr.sin_addr);
            p.peerPort = client_addr.sin_port;
            p.localIp = _P->host;
            p.localPort = _P->port;
            p.server = this;
            p.socket = client;

            TcpSocket* clientsocket = new TcpSocket(&p);
            {
                std::unique_lock<std::recursive_mutex> lock(_P->mutex);
                _P->pending.push_back(clientsocket);
                _P->clients.insert(clientsocket);
            }

            NewConnection();
        }

    }
}

void TcpServer::UnRegister(TcpSocket *socket)
{
    std::unique_lock<std::recursive_mutex> lock(_P->mutex);
    _P->clients.erase(socket);
}

TcpSocket::TcpSocket(TcpSocket::PrivateCreate *P):TcpSocket()
{
    _P->isBind = true;
    _P->isActive = true;
    _P->server = P->server;
    _P->socket = P->socket;
    _P->peerHost = P->peerIp;
    _P->peerPort = P->peerPort;
    _P->localHost = P->localIp;
    _P->localPort = P->localPort;
    _P->recvThread.reset(new std::thread(std::bind(&TcpSocket::RecvLoop,this)));
}

TcpSocket::TcpSocket():_P(new Private())
{

}

TcpSocket::~TcpSocket()
{
    Close();
}

bool TcpSocket::isActive() const
{
    std::unique_lock<std::mutex> lock(_P->mutex);

    return _P->isActive;
}

std::string TcpSocket::localHost() const
{
    std::unique_lock<std::mutex> lock(_P->mutex);

    return _P->localHost;
}

int TcpSocket::localPort() const
{
    std::unique_lock<std::mutex> lock(_P->mutex);

    return _P->localPort;
}

std::string TcpSocket::peerHost() const
{
    std::unique_lock<std::mutex> lock(_P->mutex);

    return _P->peerHost;
}

int TcpSocket::peerPort() const
{
    std::unique_lock<std::mutex> lock(_P->mutex);

    return _P->peerPort;
}

bool TcpSocket::Bind(int port,std::string ip)
{
    std::unique_lock<std::mutex> lock(_P->mutex);
    if(_P->isBind){
        return false;
    }

    _P->socket = socket(AF_INET,SOCK_STREAM,0);

    if(_P->socket < 0){
        return false;
    }

    sockaddr_in addr = {};
    addr.sin_family = AF_INET;


#ifdef WIN32
    if(ip.empty()){
        addr.sin_addr.S_un.S_addr = ntohs(INADDR_ANY);
    }
    else{
        addr.sin_addr.S_un.S_addr = inet_addr(ip.c_str());
    }
#endif

#ifdef __linux__
    if(ip.empty()){
        addr.sin_addr.s_addr = ntohs(INADDR_ANY);
    }
    else{
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
    }
#endif

	addr.sin_port = port;
	int size = sizeof(addr);

	if (addr.sin_port == 0) {
		srand(time(NULL));
		int ret = 0;
		do
		{
			addr.sin_port = rand();
			ret = bind(_P->socket, (sockaddr*)&addr, size);
		} while (ret != 0);
	}
	else {
		if (bind(_P->socket, (sockaddr*)&addr, size) != 0)
		{
			closesocket(_P->socket);
			return false;
		}
	}
    
	_P->localPort = addr.sin_port;
    _P->isBind = true;

    return true;
}

bool TcpSocket::ConnectTo(std::string ip, int port)
{
    std::unique_lock<std::mutex> lock(_P->mutex);
    if(_P->isActive){
        return false;
    }

    if(!_P->isBind){
        return false;
    }

    if(ip.empty()){
        return false;
    }

    sockaddr_in addr = {};
    addr.sin_family = AF_INET;
#ifdef WIN32
    addr.sin_addr.S_un.S_addr = inet_addr(ip.c_str());
#endif

#ifdef __linux__
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
#endif

    addr.sin_port = port;
    int size = sizeof(addr);

    if(connect(_P->socket,(sockaddr*)&addr,size) == SOCKET_ERROR){
        return false;
    }

    _P->isActive = true;
    _P->recvThread.reset(new std::thread(std::bind(&TcpSocket::RecvLoop,this)));
}

void TcpSocket::Close()
{
    _Close(true);
}

int TcpSocket::read(unsigned char *buffer, int len)
{
    std::unique_lock<std::mutex> lock(_P->mutex);
    if(!_P->isActive){
        return -1;
    }

    if(len <= 0){
        return 0;
    }

    int buffersize = _P->buffer.size();
    int ret = buffersize < len?buffersize:len;

    std::copy_n(_P->buffer.begin(),ret,buffer);
    _P->buffer.erase(_P->buffer.begin(),_P->buffer.begin() + len);

    return ret;
}

ByteArray TcpSocket::readAll()
{
    std::unique_lock<std::mutex> lock(_P->mutex);
    if(!_P->isActive){
		return {};
    }

    return std::move(_P->buffer);
}

void TcpSocket::write(const unsigned char *content, int len)
{
    std::unique_lock<std::mutex> lock(_P->mutex);
    if(!_P->isActive){
        return;
    }

    int r = send(_P->socket,(const char*)content,len,0);
}

void TcpSocket::RecvLoop()
{
    while (true) {
        if(!_P->isActive) return;
        constexpr int size = 4096;
        unsigned char buffer[size];
        int len;
        len = recv(_P->socket,(char*)buffer,size,0);

        if(!_P->isActive) return;

        if(len <= 0)
        {
            _Close(false);
            Disconnected();
            return;
        }

        {
            std::unique_lock<std::mutex> lock(_P->mutex);
			_P->buffer.insert(_P->buffer.end(), buffer, buffer + len);
        }

        ReadyRead();
    }
}

void TcpSocket::_Close(bool wait)
{
    {
        std::unique_lock<std::mutex> lock(_P->mutex);
        if(!_P->isActive){
            return;
        }

        _P->isActive = false;
        closesocket(_P->socket);
    }

    if(wait){
        _P->recvThread->join();
        _P->recvThread.reset();
    }

    {
        std::unique_lock<std::mutex> lock(_P->mutex);

        _P->buffer.clear();
        _P->isBind = false;
        _P->socket = -1;
        _P->isActive = false;
        _P->peerHost.clear();
        _P->localHost.clear();
        _P->peerPort = -1;
        _P->localPort = -1;
        if(_P->server){
            _P->server->UnRegister(this);
            _P->server = NULL;
        }

        Disconnected();
    }
}
